
#include "PortWrapper.h"
#include "BridgeWrapper.h"
#include "port.h"
#include "bridge.h"

using namespace System::Drawing::Imaging;
using namespace System::Diagnostics;

PortTree::PortTree (BRIDGE* bridge, int portIndex, int treeIndex)
{
	this->bridge = bridge;
	this->portIndex = portIndex;
	this->treeIndex = treeIndex;
}

unsigned short PortTree::portId::get ()
{
	return bridge->ports [portIndex]->trees [treeIndex]->portId;
}

PortRole PortTree::role::get ()
{
	return GetPortRole (bridge->ports [portIndex]->trees [treeIndex]->role);
}

PortRole PortTree::selectedRole::get ()
{
	return GetPortRole (bridge->ports [portIndex]->trees [treeIndex]->selectedRole);
}

PriorityVectorWrapper^ PortTree::portPriority::get ()
{
	return gcnew PriorityVectorWrapper (&bridge->ports [portIndex]->trees [treeIndex]->portPriority);
}

PriorityVectorWrapper^ PortTree::designatedPriority::get ()
{
	return gcnew PriorityVectorWrapper (&bridge->ports [portIndex]->trees [treeIndex]->designatedPriority);
}

PortRole PortTree::GetPortRole (PORT_ROLE role)
{
	if (role == PORT_ROLE_DISABLED)
		return PortRole::Disabled;
	else if (role == PORT_ROLE_ROOT)
		return PortRole::Root;
	else if (role == PORT_ROLE_DESIGNATED)
		return PortRole::Designated;
	else if (role == PORT_ROLE_ALTERNATE)
		return PortRole::Alternate;
	else if (role == PORT_ROLE_BACKUP)
		return PortRole::Backup;
	else if (role == PORT_ROLE_MASTER)
		return PortRole::Master;
	else if (role == PORT_ROLE_UNKNOWN)
		return PortRole::Unknown;
	else
	{
		assert (false);
		return PortRole::Unknown;
	}
}

unsigned char PortTree::Priority::get ()
{
	return STP_GetPortPriority (this->bridge, this->portIndex, this->treeIndex);
}

void PortTree::Priority::set (unsigned char value)
{
	STP_SetPortPriority (this->bridge, this->portIndex, this->treeIndex, value, System::Environment::TickCount);
}

// ============================================================================

void Port::SetBitmaps (array<Bitmap^>^ bitmaps, Bitmap^ bitmap)
{
	bitmaps [0] = gcnew Bitmap (bitmap);
	bitmaps [0]->RotateFlip (RotateFlipType::RotateNoneFlipNone);

	bitmaps [1] = gcnew Bitmap (bitmap);
	bitmaps [1]->RotateFlip (RotateFlipType::Rotate90FlipNone);

	bitmaps [2] = gcnew Bitmap (bitmap);
	bitmaps [2]->RotateFlip (RotateFlipType::Rotate180FlipNone);

	bitmaps [3] = gcnew Bitmap (bitmap);
	bitmaps [3]->RotateFlip (RotateFlipType::Rotate270FlipNone);

}

static Port::Port ()
{
	Pen^ penDiscardingPort = gcnew Pen (Color::Red, 1);
	Brush^ brushDiscardingPort = Brushes::Red;

	Pen^ penLearningPort = gcnew Pen (Color::Yellow, 1);
	Brush^ brushLearningPort = Brushes::Yellow;

	Pen^ penForwardingPort = gcnew Pen (Color::Green, 1);
	Brush^ brushForwardingPort = Brushes::Green;

	float circleDiameter = ((ExteriorDrawingHeight / 2) < ExteriorDrawingWidth ? (ExteriorDrawingHeight / 2) : ExteriorDrawingWidth) - 2;

	float discardingFirstHorizontalLineY = circleDiameter + (ExteriorDrawingHeight - circleDiameter) / 3;
	float discardingSecondHorizontalLineY = circleDiameter + (ExteriorDrawingHeight - circleDiameter) * 2 / 3;
	float learningHorizontalLineY = circleDiameter + (ExteriorDrawingHeight - circleDiameter) / 2;

	Bitmap^ bitmap = gcnew Bitmap ((int) ExteriorDrawingWidth, (int) ExteriorDrawingHeight, PixelFormat::Format32bppArgb);
	Graphics^ g = Graphics::FromImage (bitmap);

	// ------------------------------------------------------------------------
	// disabled
	g->Clear (Color::Transparent);
	g->DrawLine (penDiscardingPort, ExteriorDrawingWidth / 2, 0.0, ExteriorDrawingWidth / 2, ExteriorDrawingHeight);
	g->DrawLine (penDiscardingPort, 0.0, ExteriorDrawingHeight / 3, ExteriorDrawingWidth, ExteriorDrawingHeight * 2 / 3);
	SetBitmaps (bitmapsDisabled, bitmap);

	// ------------------------------------------------------------------------
	// designated discarding
	g->Clear (Color::Transparent);
	g->DrawLine (penDiscardingPort, ExteriorDrawingWidth / 2, circleDiameter, ExteriorDrawingWidth / 2, ExteriorDrawingHeight);
	g->FillEllipse (brushDiscardingPort, ExteriorDrawingWidth / 2 - circleDiameter / 2, 0.0, circleDiameter, circleDiameter);
	g->DrawLine (penDiscardingPort, 0.0, discardingFirstHorizontalLineY, ExteriorDrawingWidth, discardingFirstHorizontalLineY);
	g->DrawLine (penDiscardingPort, 0.0, discardingSecondHorizontalLineY, ExteriorDrawingWidth, discardingSecondHorizontalLineY);
	SetBitmaps (bitmapsDesignatedDiscarding, bitmap);

	// designated learning
	g->Clear (Color::Transparent);
	g->DrawLine (penLearningPort, ExteriorDrawingWidth / 2, circleDiameter, ExteriorDrawingWidth / 2, ExteriorDrawingHeight);
	g->FillEllipse (brushLearningPort, ExteriorDrawingWidth / 2 - circleDiameter / 2, 0.0, circleDiameter, circleDiameter);
	g->DrawLine (penLearningPort, 0.0, learningHorizontalLineY, ExteriorDrawingWidth, learningHorizontalLineY);
	SetBitmaps (bitmapsDesignatedLearning, bitmap);

	// designated forwarding
	g->Clear (Color::Transparent);
	g->DrawLine (penForwardingPort, ExteriorDrawingWidth / 2, circleDiameter, ExteriorDrawingWidth / 2, ExteriorDrawingHeight);
	g->FillEllipse (brushForwardingPort, ExteriorDrawingWidth / 2 - circleDiameter / 2, 0.0, circleDiameter, circleDiameter);
	SetBitmaps (bitmapsDesignatedForwarding, bitmap);

	// designated forwarding operEdge
	g->Clear (Color::Transparent);
	g->FillEllipse (brushForwardingPort, ExteriorDrawingWidth / 2 - circleDiameter / 2, 0.0, circleDiameter, circleDiameter);
	g->DrawLine (penForwardingPort, ExteriorDrawingWidth / 2, circleDiameter, 1.0, circleDiameter + (ExteriorDrawingHeight - circleDiameter) / 2);
	g->DrawLine (penForwardingPort, ExteriorDrawingWidth / 2, circleDiameter, ExteriorDrawingWidth - 1, circleDiameter + (ExteriorDrawingHeight - circleDiameter) / 2);
	g->DrawLine (penForwardingPort, ExteriorDrawingWidth / 2, ExteriorDrawingHeight, 1.0, circleDiameter + (ExteriorDrawingHeight - circleDiameter) / 2);
	g->DrawLine (penForwardingPort, ExteriorDrawingWidth / 2, ExteriorDrawingHeight, ExteriorDrawingWidth - 1, circleDiameter + (ExteriorDrawingHeight - circleDiameter) / 2);
	SetBitmaps (bitmapsDesignatedForwardingOperEdge, bitmap);

	// ------------------------------------------------------------------------
	// root or master discarding
	g->Clear (Color::Transparent);
	g->DrawLine (penDiscardingPort, ExteriorDrawingWidth / 2, circleDiameter, ExteriorDrawingWidth / 2, ExteriorDrawingHeight);
	g->DrawEllipse (penDiscardingPort, ExteriorDrawingWidth / 2 - circleDiameter / 2, 0.0, circleDiameter, circleDiameter);
	g->DrawLine (penDiscardingPort, 0.0, discardingFirstHorizontalLineY, ExteriorDrawingWidth, discardingFirstHorizontalLineY);
	g->DrawLine (penDiscardingPort, 0.0, discardingSecondHorizontalLineY, ExteriorDrawingWidth, discardingSecondHorizontalLineY);
	SetBitmaps (bitmapsRootOrMasterDiscarding, bitmap);

	// root or master learning
	g->Clear (Color::Transparent);
	g->DrawLine (penLearningPort, ExteriorDrawingWidth / 2, circleDiameter, ExteriorDrawingWidth / 2, ExteriorDrawingHeight);
	g->DrawEllipse (penLearningPort, ExteriorDrawingWidth / 2 - circleDiameter / 2, 0.0, circleDiameter, circleDiameter);
	g->DrawLine (penLearningPort, 0.0, learningHorizontalLineY, ExteriorDrawingWidth, learningHorizontalLineY);
	SetBitmaps (bitmapsRootOrMasterLearning, bitmap);

	// root or master forwarding
	g->Clear (Color::Transparent);
	g->DrawLine (penForwardingPort, ExteriorDrawingWidth / 2, circleDiameter, ExteriorDrawingWidth / 2, ExteriorDrawingHeight);
	g->DrawEllipse (penForwardingPort, ExteriorDrawingWidth / 2 - circleDiameter / 2, 0.0, circleDiameter, circleDiameter);
	SetBitmaps (bitmapsRootOrMasterForwarding, bitmap);

	// ------------------------------------------------------------------------
	// Alternate discarding
	g->Clear (Color::Transparent);
	g->DrawLine (penDiscardingPort, ExteriorDrawingWidth / 2, 0.0, ExteriorDrawingWidth / 2, ExteriorDrawingHeight);
	g->DrawLine (penDiscardingPort, 0.0, discardingFirstHorizontalLineY, ExteriorDrawingWidth, discardingFirstHorizontalLineY);
	g->DrawLine (penDiscardingPort, 0.0, discardingSecondHorizontalLineY, ExteriorDrawingWidth, discardingSecondHorizontalLineY);
	SetBitmaps (bitmapsAlternateDiscarding, bitmap);

	// Alternate learning
	g->Clear (Color::Transparent);
	g->DrawLine (penLearningPort, ExteriorDrawingWidth / 2, 0.0, ExteriorDrawingWidth / 2, ExteriorDrawingHeight);
	g->DrawLine (penLearningPort, 0.0, learningHorizontalLineY, ExteriorDrawingWidth, learningHorizontalLineY);
	SetBitmaps (bitmapsAlternateLearning, bitmap);

	// ------------------------------------------------------------------------
	// Backup discarding
	g->Clear (Color::Transparent);
	g->DrawLine (penDiscardingPort, ExteriorDrawingWidth / 2, 0.0, ExteriorDrawingWidth / 2, ExteriorDrawingHeight);
	g->DrawLine (penDiscardingPort, 0.0, discardingFirstHorizontalLineY / 2, ExteriorDrawingWidth, discardingFirstHorizontalLineY / 2);
	g->DrawLine (penDiscardingPort, 0.0, discardingFirstHorizontalLineY, ExteriorDrawingWidth, discardingFirstHorizontalLineY);
	g->DrawLine (penDiscardingPort, 0.0, discardingSecondHorizontalLineY, ExteriorDrawingWidth, discardingSecondHorizontalLineY);
	SetBitmaps (bitmapsBackupDiscarding, bitmap);

	// ------------------------------------------------------------------------
	// undefined
	g->Clear (Color::Transparent);
	g->DrawLine (penDiscardingPort, ExteriorDrawingWidth / 2, 0.0, ExteriorDrawingWidth / 2, ExteriorDrawingHeight);
	g->DrawString (L"?", SystemFonts::DialogFont, brushDiscardingPort, 0, 0);
	SetBitmaps (bitmapsUndefined, bitmap);
}

// ============================================================================

Port::Port (BinaryReader^ br, BRIDGE* bridge, int portIndex)
{
	this->bridge = bridge;
//	this->portIndex = portIndex;
	this->port = bridge->ports [portIndex];

	this->trees = gcnew array<PortTree^> (bridge->treeCount);
	for (int treeIndex = 0; treeIndex < bridge->treeCount; treeIndex++)
		this->trees [treeIndex] = gcnew PortTree (bridge, portIndex, treeIndex);

	if (br == nullptr)
	{
		side = Port::Side::Bottom;
		offset = (5 + Port::InteriorDrawingLongSize + 5) * portIndex + 5 + Port::InteriorDrawingLongSize / 2;
		Name = String::Format (L"Port{0}", (System::Char) ('A' + portIndex));
	}
	else
	{
		side = (Port::Side) br->ReadByte ();
		offset = br->ReadSingle ();
		Name = br->ReadString ();
	}
}


void Port::Serialize (BinaryWriter^ bw)
{
	bw->Write ((Byte) side);
	bw->Write (offset);
	bw->Write (Name);
}

// ============================================================================

bool Port::portEnabled::get ()
{
	return port->portEnabled;
}

bool Port::operPointToPointMAC::get ()
{
	return port->operPointToPointMAC;
}

String^ Port::Name::get ()
{
	return gcnew String (port->debugName);
}

void Port::Name::set (String^ newName)
{
	IntPtr str = System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi (newName);
	STP_SetPortName (bridge, portIndex, (const char*) str.ToPointer ());
	System::Runtime::InteropServices::Marshal::FreeHGlobal (str);
}

bool Port::operEdge::get ()
{
	return bridge->ports [portIndex]->operEdge;
}

Bitmap^ Port::GetBitmap (PORT_ROLE role, bool learning, bool forwarding, bool operEdge, RotateFlipType rotateFlip)
{
	int bitmapIndex;
	if (rotateFlip == RotateFlipType::RotateNoneFlipNone)
		bitmapIndex = 0;
	else if (rotateFlip == RotateFlipType::Rotate90FlipNone)
		bitmapIndex = 1;
	else if (rotateFlip == RotateFlipType::Rotate180FlipNone)
		bitmapIndex = 2;
	else if (rotateFlip == RotateFlipType::Rotate270FlipNone)
		bitmapIndex = 3;
	else
		throw gcnew Exception ();

	if (role == PORT_ROLE_DISABLED)
		return bitmapsDisabled [bitmapIndex];

	else if ((role == PORT_ROLE_DESIGNATED) && !learning && !forwarding)
		return bitmapsDesignatedDiscarding [bitmapIndex];
	else if ((role == PORT_ROLE_DESIGNATED) && learning && !forwarding)
		return bitmapsDesignatedLearning [bitmapIndex];
	else if ((role == PORT_ROLE_DESIGNATED) && learning && forwarding && !operEdge)
		return bitmapsDesignatedForwarding [bitmapIndex];
	else if ((role == PORT_ROLE_DESIGNATED) && learning && forwarding && operEdge)
		return bitmapsDesignatedForwardingOperEdge [bitmapIndex];

	else if (((role == PORT_ROLE_ROOT) || (role == PORT_ROLE_MASTER)) && !learning && !forwarding)
		return bitmapsRootOrMasterDiscarding [bitmapIndex];
	else if (((role == PORT_ROLE_ROOT) || (role == PORT_ROLE_MASTER)) && learning && !forwarding)
		return bitmapsRootOrMasterLearning [bitmapIndex];
	else if (((role == PORT_ROLE_ROOT) || (role == PORT_ROLE_MASTER)) && learning && forwarding)
		return bitmapsRootOrMasterForwarding [bitmapIndex];

	else if ((role == PORT_ROLE_ALTERNATE) && !learning && !forwarding)
		return bitmapsAlternateDiscarding [bitmapIndex];
	else if ((role == PORT_ROLE_ALTERNATE) && learning && !forwarding)
		return bitmapsAlternateLearning [bitmapIndex];

	else if ((role == PORT_ROLE_BACKUP) && !learning && !forwarding)
		return bitmapsBackupDiscarding [bitmapIndex];

	else if (role == PORT_ROLE_UNKNOWN)
		return bitmapsUndefined [bitmapIndex];

	else
		throw gcnew NotImplementedException ();
}

Bitmap^ Port::GetBitmap (int treeIndex, RotateFlipType rotateFlip)
{
	PORT_ROLE role = port->trees [treeIndex]->role;
	bool learning = port->trees [treeIndex]->learning;
	bool forwarding = port->trees [treeIndex]->forwarding;

	return GetBitmap (role, learning, forwarding, port->operEdge, rotateFlip);
}

